/**************************************************************************/
/*                                                                        */
/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
/*                                                                        */
/*       This software is licensed under the Microsoft Software License   */
/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
/*       and in the root directory of this software.                      */
/*                                                                        */
/**************************************************************************/


/**************************************************************************/
/**************************************************************************/
/**                                                                       */
/** NetX Secure Component                                                 */
/**                                                                       */
/**    Transport Layer Security (TLS)                                     */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/

#define NX_SECURE_SOURCE_CODE

#include "nx_secure_tls.h"
#ifdef NX_SECURE_ENABLE_DTLS
#include "nx_secure_dtls.h"
#endif /* NX_SECURE_ENABLE_DTLS */

/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _nx_secure_tls_handshake_hash_update                PORTABLE C      */
/*                                                           6.1.9        */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Timothy Stapko, Microsoft Corporation                               */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function updates the hash function states with handshake       */
/*    messages, both generated by this host and received from a remote    */
/*    host. This is needed for the TLS Finished message handshake hash.   */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    tls_session                           TLS control block             */
/*    data                                  Data to be hashed             */
/*    length                                Data length                   */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    status                                Completion status             */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    [nx_crypto_operation]                 Hash update functions         */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    _nx_secure_dtls_send_clienthello      Send ClientHello              */
/*    _nx_secure_dtls_send_handshake_record Send DTLS handshake record    */
/*    _nx_secure_dtls_server_handshake      DTLS server state machine     */
/*    _nx_secure_tls_send_clienthello       Send ClientHello              */
/*    _nx_secure_tls_send_handshake_record  Send TLS handshake record     */
/*    _nx_secure_tls_server_handshake       TLS server state machine      */
/*                                                                        */
/*  RELEASE HISTORY                                                       */
/*                                                                        */
/*    DATE              NAME                      DESCRIPTION             */
/*                                                                        */
/*  05-19-2020     Timothy Stapko           Initial Version 6.0           */
/*  09-30-2020     Timothy Stapko           Modified comment(s),          */
/*                                            resulting in version 6.1    */
/*  10-15-2021     Timothy Stapko           Modified comment(s), fixed    */
/*                                            compilation issue with      */
/*                                            TLS 1.3 and disabling TLS   */
/*                                            server,                     */
/*                                            resulting in version 6.1.9  */
/*                                                                        */
/**************************************************************************/
UINT  _nx_secure_tls_handshake_hash_update(NX_SECURE_TLS_SESSION *tls_session, UCHAR *data,
                                           UINT length)
{
UINT                    status = NX_NOT_SUCCESSFUL;
const NX_CRYPTO_METHOD *method_ptr;

#if (NX_SECURE_TLS_TLS_1_3_ENABLED)
UINT                    hash_length;
USHORT                  hello_retry_process = 0;
#endif

#if (NX_SECURE_TLS_TLS_1_3_ENABLED)
    if(tls_session->nx_secure_tls_1_3)
    {
        /* Hash handshake record using ciphersuite hash routine. */
        if (tls_session -> nx_secure_tls_session_ciphersuite == NX_NULL)
        {
            /* Set the hash method to the default of SHA-256 if no ciphersuite is available. */
            method_ptr = tls_session -> nx_secure_tls_crypto_table->nx_secure_tls_handshake_hash_sha256_method;

        }
        else
        {
            /* The handshake transcript hash in TLS 1.3 uses the hash routine associated with the chosen ciphersuite. */
            method_ptr = tls_session -> nx_secure_tls_session_ciphersuite -> nx_secure_tls_hash;
        }

        /* Generate the "transcript hash" for the point in the handshake where this message falls.
         * This is needed for TLS 1.3 key generation which uses the hash of all previous handshake
         * messages at multiple points during the handshake. Instead of saving the entirety of the
         * handshake messages, just generate a hash when each record is hashed. */
        if (method_ptr -> nx_crypto_operation != NX_NULL)
        {
            status = method_ptr -> nx_crypto_operation(NX_CRYPTO_HASH_UPDATE,
                                                       tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_sha256_handler,
                                                       (NX_CRYPTO_METHOD*)method_ptr,
                                                       NX_NULL,
                                                       0,
                                                       data,
                                                       length,
                                                       NX_NULL,
                                                       NX_NULL,
                                                       0,
                                                       tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_sha256_metadata,
                                                       tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_sha256_metadata_size,
                                                       NX_NULL,
                                                       NX_NULL);

            /* See if we have recieved or are sending a hello retry message - we need to process the hash differently. */
#ifndef NX_SECURE_TLS_SERVER_DISABLED
            if (tls_session -> nx_secure_tls_socket_type == NX_SECURE_TLS_SESSION_TYPE_SERVER)
            {
                hello_retry_process = (tls_session -> nx_secure_tls_server_state == NX_SECURE_TLS_SERVER_STATE_SEND_HELLO_RETRY);
            }
#endif
#ifndef NX_SECURE_TLS_CLIENT_DISABLED
            if (tls_session -> nx_secure_tls_socket_type == NX_SECURE_TLS_SESSION_TYPE_CLIENT)
            {
                hello_retry_process = (tls_session -> nx_secure_tls_client_state == NX_SECURE_TLS_CLIENT_STATE_HELLO_RETRY);
            }
#endif

           /* Modify message buffer - replace message with hash used for HelloRetryRequest processing. */
            if ((status == NX_SUCCESS) &&
                (hello_retry_process) &&
                (data[0] == NX_SECURE_TLS_CLIENT_HELLO) &&
                (length > NX_SECURE_TLS_HANDSHAKE_HEADER_SIZE))
            {

                /* ClientHello1 is replaced with "message_hash || 00 00 Hash.length || Hash(ClientHello1)"*/
                status = method_ptr -> nx_crypto_operation(NX_CRYPTO_HASH_CALCULATE,
                                                           tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_sha256_handler,
                                                           (NX_CRYPTO_METHOD*)method_ptr,
                                                           NX_NULL,
                                                           0,
                                                           NX_NULL,
                                                           0,
                                                           NX_NULL,
                                                           data + 4, /* Reuse input buffer since the input will not be used anymore. */
                                                           NX_SECURE_TLS_MAX_HASH_SIZE,
                                                           tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_sha256_metadata,
                                                           tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_sha256_metadata_size,
                                                           NX_NULL,
                                                           NX_NULL);

                /* Initialize handshake HASH again. */
                if (status == NX_SUCCESS)
                {
                    status = _nx_secure_tls_handshake_hash_init(tls_session);
                }

                if (status == NX_SUCCESS)
                {
                    hash_length = method_ptr -> nx_crypto_ICV_size_in_bits >> 3;
                    data[0] = NX_SECURE_TLS_MESSAGE_HASH & 0xFF;
                    data[1] = 0;
                    data[2] = 0;
                    data[3] = hash_length & 0xFF;

                    status = method_ptr -> nx_crypto_operation(NX_CRYPTO_HASH_UPDATE,
                                                               tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_sha256_handler,
                                                               (NX_CRYPTO_METHOD*)method_ptr,
                                                               NX_NULL,
                                                               0,
                                                               data,
                                                               hash_length + 4,
                                                               NX_NULL,
                                                               NX_NULL,
                                                               0,
                                                               tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_sha256_metadata,
                                                               tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_sha256_metadata_size,
                                                               NX_NULL,
                                                               NX_NULL);
                }
            }
        }

        return(status);


    }
#endif

    /* Hash this handshake message. We do not hash HelloRequest messages, but since only the server will send them,
       we do not worry about them here because these are only messages received from the client at this point.
       Hashes include the handshake layer header but not the record layer header. */
#if (NX_SECURE_TLS_TLS_1_2_ENABLED)
#ifdef NX_SECURE_ENABLE_DTLS
    if (tls_session -> nx_secure_tls_protocol_version == NX_SECURE_TLS_VERSION_TLS_1_2 ||
        tls_session -> nx_secure_tls_protocol_version == NX_SECURE_DTLS_VERSION_1_2)
#else
    if (tls_session -> nx_secure_tls_protocol_version == NX_SECURE_TLS_VERSION_TLS_1_2)
#endif /* NX_SECURE_ENABLE_DTLS */
    {
        /* Hash is determined by ciphersuite in TLS 1.2. Default is SHA-256. */
        method_ptr = tls_session -> nx_secure_tls_crypto_table -> nx_secure_tls_handshake_hash_sha256_method;
        if (method_ptr -> nx_crypto_operation != NX_NULL)
        {
            status = method_ptr -> nx_crypto_operation(NX_CRYPTO_HASH_UPDATE,
                                                       tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_sha256_handler,
                                                       (NX_CRYPTO_METHOD*)method_ptr,
                                                       NX_NULL,
                                                       0,
                                                       data,
                                                       length,
                                                       NX_NULL,
                                                       NX_NULL,
                                                       0,
                                                       tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_sha256_metadata,
                                                       tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_sha256_metadata_size,
                                                       NX_NULL,
                                                       NX_NULL);

            if (status != NX_CRYPTO_SUCCESS)
            {
                return(status);
            }
        }
    }
#endif

#if (NX_SECURE_TLS_TLS_1_0_ENABLED || NX_SECURE_TLS_TLS_1_1_ENABLED)
#ifdef NX_SECURE_ENABLE_DTLS
    if (tls_session -> nx_secure_tls_protocol_version == NX_SECURE_TLS_VERSION_TLS_1_0 ||
        tls_session -> nx_secure_tls_protocol_version == NX_SECURE_TLS_VERSION_TLS_1_1 ||
        tls_session -> nx_secure_tls_protocol_version == NX_SECURE_DTLS_VERSION_1_0)
#else
    if (tls_session -> nx_secure_tls_protocol_version == NX_SECURE_TLS_VERSION_TLS_1_0 ||
        tls_session -> nx_secure_tls_protocol_version == NX_SECURE_TLS_VERSION_TLS_1_1)
#endif /* NX_SECURE_ENABLE_DTLS */
    {
        /* TLS 1.0 and 1.1 use both MD5 and SHA-1. */
        method_ptr = tls_session -> nx_secure_tls_crypto_table -> nx_secure_tls_handshake_hash_md5_method;
        if (method_ptr -> nx_crypto_operation != NX_NULL)
        {
            status = method_ptr -> nx_crypto_operation(NX_CRYPTO_HASH_UPDATE,
                                                       tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_md5_handler,
                                                       (NX_CRYPTO_METHOD*)method_ptr,
                                                       NX_NULL,
                                                       0,
                                                       data,
                                                       length,
                                                       NX_NULL,
                                                       NX_NULL,
                                                       0,
                                                       tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_md5_metadata,
                                                       tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_md5_metadata_size,
                                                       NX_NULL,
                                                       NX_NULL);

            if (status != NX_CRYPTO_SUCCESS)
            {
                return(status);
            }
        }

        method_ptr = tls_session -> nx_secure_tls_crypto_table -> nx_secure_tls_handshake_hash_sha1_method;
        if (method_ptr -> nx_crypto_operation != NX_NULL)
        {
            status = method_ptr -> nx_crypto_operation(NX_CRYPTO_HASH_UPDATE,
                                                       tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_sha1_handler,
                                                       (NX_CRYPTO_METHOD*)method_ptr,
                                                       NX_NULL,
                                                       0,
                                                       data,
                                                       length,
                                                       NX_NULL,
                                                       NX_NULL,
                                                       0,
                                                       tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_sha1_metadata,
                                                       tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_sha1_metadata_size,
                                                       NX_NULL,
                                                       NX_NULL);

            if (status != NX_CRYPTO_SUCCESS)
            {
                return(status);
            }
        }
    }
#endif

    return(status);
}

